namespace Wigy.TimeInsight
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;

    internal sealed class LocalWindowsHook : IDisposable
    {
        private IntPtr _hhook = IntPtr.Zero;

        public delegate void HookEventHandler(object sender, HookEventArgs e);

        public event HookEventHandler HookInvoked;

        public void Install(NativeMethods.HookType hookType)
        {
            // See http://stackoverflow.com/questions/10516448/error-when-using-setwindowshookex-in-windows-xp-but-not-in-windows-7
            var hModule = NativeMethods.LoadLibrary("user32.dll");
            _hhook = NativeMethods.SetWindowsHookEx(hookType, CoreHookProc, hModule, 0);

            if (_hhook != IntPtr.Zero) return;

            var error = Marshal.GetLastWin32Error();
            Console.Error.WriteLine(error);
        }

        public void Dispose()
        {
            if (_hhook != IntPtr.Zero)
            {
                NativeMethods.UnhookWindowsHookEx(_hhook);
                _hhook = IntPtr.Zero;
            }
        }

        private int CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code < 0)
                return NativeMethods.CallNextHookEx(_hhook, code, wParam, lParam);

            Debug.WriteLineIf(!Enum.IsDefined(typeof(NativeMethods.HookType), code), string.Format("Unknown windows hook type code {0}", code));

            // Let clients determine what to do
            if (HookInvoked != null)
                HookInvoked(this, new HookEventArgs((NativeMethods.HookType)code, wParam, lParam));

            // Yield to the next hook in the chain
            return NativeMethods.CallNextHookEx(_hhook, code, wParam, lParam);
        }
    }
}